package com.facebook.app.shared.clustering;

import java.io.Serializable;
import java.util.Arrays;

public class Cluster implements Clusterable, Serializable {

	private static final long serialVersionUID = 64068656176300801L;
	
	private Clusterable leftChild;
	private Clusterable rightChild;
	
	// may not be transient if clustering is split into several requests
	private double[] characteristicClusterVector;
	private transient double similarity;

	public Cluster(Clusterable leftChild, Clusterable rightChild,
			double similarity) {
		this.leftChild = leftChild;
		this.rightChild = rightChild;
		this.similarity = similarity;
		characteristicClusterVector = mergeCharacteristicClusterVectors();
	}
	
	@SuppressWarnings("unused")
	private Cluster() {}

	public Clusterable getLeftChild() {
		return leftChild;
	}

	public Clusterable getRightChild() {
		return rightChild;
	}

	public double getSimilarity() {
		return similarity;
	}

	@Override
	public double[] getCharacteristicClusterVector() {
		return characteristicClusterVector;
	}
	
	@Override
	public String toString() {
		return "Cluster [leftChild=" + getLeftChild() + ", rightChild=" + getRightChild()
				+ ", similarity=" + getSimilarity() + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(characteristicClusterVector);
		result = prime * result
				+ ((leftChild == null) ? 0 : leftChild.hashCode());
		result = prime * result
				+ ((rightChild == null) ? 0 : rightChild.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cluster other = (Cluster) obj;
		if (!Arrays.equals(characteristicClusterVector,
				other.characteristicClusterVector))
			return false;
		if (leftChild == null) {
			if (other.leftChild != null)
				return false;
		} else if (!leftChild.equals(other.leftChild))
			return false;
		if (rightChild == null) {
			if (other.rightChild != null)
				return false;
		} else if (!rightChild.equals(other.rightChild))
			return false;
		return true;
	}
	
	/**
	 * Merges the characteristicClusterVectors of the Clusterable children.
	 * 
	 * @return the merged characteristicClusterVector
	 */
	private double[] mergeCharacteristicClusterVectors() {

		double[] firstCharacteristicClusterVector = leftChild
				.getCharacteristicClusterVector();
		double[] secondCharacteristicClusterVector = rightChild
				.getCharacteristicClusterVector();

		if (firstCharacteristicClusterVector.length != secondCharacteristicClusterVector.length) {
			throw new IllegalArgumentException(
					"The lengths of the characteristicClusterVectors are different: "
							+ firstCharacteristicClusterVector.length
							+ " != "
							+ secondCharacteristicClusterVector.length);
		}

		double[] resultVector = new double[firstCharacteristicClusterVector.length];

		for (int i = 0; i < resultVector.length; i++) {
			resultVector[i] = (firstCharacteristicClusterVector[i] + secondCharacteristicClusterVector[i]) / 2.0;
		}

		return resultVector;
	}

}
